import requests
import uuid
import time
import hashlib
import base64
import json
import random
from cryptography.hazmat.primitives import hashes, serialization
from cryptography.hazmat.primitives.asymmetric import rsa, padding
from cryptography.hazmat.backends import default_backend



class WXPAY:
    def __init__(self) -> None:
        self.prepay_url = "https://api.mch.weixin.qq.com/v3/pay/transactions/jsapi"
        self.appid = "wxc0d9041b6ae1cf68"
        self.mchid = "1694657009"
        self.serial_no = "6FBADD7A81CBC3BD78033E63ACC9A6AD78BC3F06"

    def get_pay_info(self, order_id, desc, price, callback, open_id, attach):
        data = {
            "mchid": self.mchid,
            "out_trade_no": order_id,
            "appid": self.appid,
            "description": desc,
            "notify_url": callback,
            "amount": {
                "total": price,
                "currency": "CNY"
            },
            "payer": {
                "openid": open_id
            },
            "attach": attach
        }
        timestamp = str(int(time.time()))
        nonce_str = self.get_noncestr()
        prepay_sign = self.get_prepay_sign(req_method="POST", abs_url="/v3/pay/transactions/jsapi", timestamp=timestamp, nonce_str=nonce_str, body=data)
        Authorization = f'WECHATPAY2-SHA256-RSA2048 mchid="{self.mchid}",nonce_str="{nonce_str}",signature="{prepay_sign}",timestamp="{timestamp}",serial_no="{self.serial_no}"'
        headers = {
            "Authorization": Authorization
        }
        res = requests.post(url=self.prepay_url, headers=headers, json=data)
        res.raise_for_status()
        return {
            "prepay_id" : res.json().get("prepay_id"),
            "nonce_str" : nonce_str,
            "timestamp": timestamp,
            "sign": self.get_pay_sign(timestamp=timestamp, nonce_str=nonce_str, prepay_id=res.json().get("prepay_id"))
        }

    def get_prepay_sign(self, req_method, abs_url, timestamp,nonce_str, body=''):
        """
        签名生成步骤: https://pay.weixin.qq.com/docs/merchant/development/interface-rules/signature-generation.html
        """
        with open("apiclient_key.pem", 'rb') as key_file:
            private_key = serialization.load_pem_private_key(
                key_file.read(),
                password=None,  # 如果私钥是加密的，这里需要提供密码
            )
        nonce_str=nonce_str
        message = self.build_message(method=req_method, url_path=abs_url, timestamp=timestamp, nonce_str=nonce_str, body=body)
        # 计算SHA-256哈希

        # 使用RSA私钥进行签名
        signature = private_key.sign(
            message.encode('utf-8'),
            padding.PKCS1v15(),
            hashes.SHA256()
        )

        # 对签名结果进行Base64编码
        base64_signature = base64.b64encode(signature).decode('utf-8')
        return base64_signature

    def get_pay_sign(self, timestamp, nonce_str, prepay_id):
        """
        小程序appId
        时间戳
        随机字符串
        订单详情扩展字符串
        """
        with open("apiclient_key.pem", 'rb') as key_file:
            private_key = serialization.load_pem_private_key(
                key_file.read(),
                password=None,  # 如果私钥是加密的，这里需要提供密码
            )
        nonce_str=nonce_str
        message = f"{self.appid}\n{timestamp}\n{nonce_str}\nprepay_id={prepay_id}\n"
        # 计算SHA-256哈希

        # 使用RSA私钥进行签名
        signature = private_key.sign(
            message.encode('utf-8'),
            padding.PKCS1v15(),
            hashes.SHA256()
        )

        # 对签名结果进行Base64编码
        base64_signature = base64.b64encode(signature).decode('utf-8')
        return base64_signature        

    @staticmethod
    def get_noncestr():
        data = "123456789zxcvbnmasdfghjklqwertyuiopZXCVBNMASDFGHJKLQWERTYUIOP"
        nonce_str = ''.join(random.sample(data, 30))
        return nonce_str    

    @staticmethod
    def build_message(method, url_path, timestamp, nonce_str, body):
        """
        生成【请求签名串】
        :param method: 获取HTTP请求的方法（GET，POST，PUT）
        :param url: 获取请求的绝对URL，并去除域名部分得到参与签名的URL。如果请求中有查询参数，URL末尾应附加有'?'和对应的查询字符串。
        :param timestamp: 获取发起请求时的系统当前时间戳(秒)
        :param nonce_str: 随机串
        :param body: 获取请求中的请求报文主体，请求方法为GET时，报文主体为空，当请求方法为POST或PUT时，请使用真实发送的JSON报文。 图片上传API，请使用meta对应的JSON报文
        :return:
        """
        if method == 'GET' or body is None:
            body = ''
        return f'{method}\n{url_path}\n{timestamp}\n{nonce_str}\n{json.dumps(body)}\n'


if __name__ == '__main__':
    WXPAY().get_pay_info(order_id=''.join(random.sample("132456789", 6)), desc="test", price=1, callback="https://baidu.com", open_id="oZnqZ5eGM3pu6EFciAiZhD84MMCM", attach='')









